#
# Copyright (c) 2011, EPFL (Ecole Politechnique Federale de Lausanne)
# All rights reserved.
#
# Created by Marco Canini, Daniele Venzano, Dejan Kostic, Jennifer Rexford
# Contributed to this file: Peter Peresini, Maciej Kuzniar
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#   -  Redistributions of source code must retain the above copyright notice,
#      this list of conditions and the following disclaimer.
#   -  Redistributions in binary form must reproduce the above copyright notice,
#      this list of conditions and the following disclaimer in the documentation
#      and/or other materials provided with the distribution.
#   -  Neither the names of the contributors, nor their associated universities or
#      organizations may be used to endorse or promote products derived from this
#      software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
# SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#

from lib.node import Node
from invariants.violation import Violation
from invariants.invariant_dispatcher import testPoint, reportViolation

import nox.lib.openflow as openflow
import nox.lib.core as core
import nox.lib.util as of_util
import time
from nox.lib.packet.mac_address import MacAddress
from nox.lib.packet.ip_address import IpAddress
from nox.lib.packet import ethernet
from lib.of_packet_out_message import PacketOutMessage
from lib.of_modify_state_message import ModifyStateMessage
from lib.of_flow_table_modification_message import FlowTableModificationMessage

import logging
import utils


	#------------------CHANGE------------------------------

	"""
		Adding up an extra field in the flow table entry
		as       VERSION_NUMBER 
	"""
class FlowTableEntry:
	def __init__(self, attrs, actions, priority, send_flow_rem=False,version_number=None):
		self.attrs = attrs
		self.actions = actions
		self.priority = priority				# 
		self.send_flow_rem = send_flow_rem

		# ----------CHANGE------------
		self.version_number = version_number
		self.waiting_list = []			# a list of buffer_id, those packets are stored in buffer
		# ----------------------------

	def __lt__(self, other):
		my_attrs = utils.flatten_dict(self.attrs)
		other_attrs = utils.flatten_dict(other.attrs)


		#-------------CHANGE-------------------------------
		
		my_version = self.version_number
		other_version = other.version_number
		
		#TODO :
		'''
			check wether there the version number is greater than or less
			and on that basis install the place holder value in the flow table 
			entry in the flow table 
		'''	
		if my_version == other_version:
			'''
				Comparing bewtween actions and attrib only 
				makes sense when they are of the same versions. 
			'''
			if my_attrs != other_attrs:
				return my_attrs < other_attrs
			else:
				return self.actions < other.actions
		else :
			return True			# TODO : problem that needs to solve

	def __eq__(self, other):
		eq = True
		eq = eq and self.attrs == other.attrs
		eq = eq and self.actions == other.actions
		eq = eq and self.priority == other.priority
		eq = eq and self.send_flow_rem == other.send_flow_rem

		#---------------CHANGE-------------------------------
		eq = eq and self.version_number == other.version_number
		#----------------------------------------------------

		return eq

	def __ne__(self, other):
		return not self.__eq__(other)

	def __getstate__(self):
		filtered_dict = {}
		filtered_dict["actions"] = self.actions
		filtered_dict["attrs"] = utils.serialize_dict(self.attrs)
		filtered_dict["priority"] = self.priority
		filtered_dict["send_flow_rem"] = self.send_flow_rem
		#----------------CHANGE------------------------------------
		filtered_dict["version_number"] = self.version_number
		filtered_dict["waiting_list"] = self.waiting_list
		#-----------------------------------------------------------
		return filtered_dict

	def __repr__(self):
		# ---------------CHANGE-----------
		return str((self.attrs, self.actions, self.priority. self.version_number,self.waiting_list))
		# --------------------------------

class OpenflowSwitch(Node):
	ALWAYS_NEW_STATE = False

	def __init__(self, name, port_count, of_id, expire_entries=False):
		Node.__init__(self, name, port_count)
		self.log = logging.getLogger("nice.mc.%s" % self.name)
		self.flow_table_object = []
		self.controller = None
		self.openflow_id = of_id
		self.buffers = []
		self.next_buffer_id = 0
		self.packet_store = {}
		self.command_queue = []
		self.fault_injection_count = 0
		self.state_cnt = 0
		self.expire_entries = expire_entries
		#----------------CHANGE-------------------------------
		# self.flow_packet_buffer = []
		#-----------------------------------------------------

	@property
	def flow_table(self):
#		self.communicationObjectUsed(self, self.name + ".flowTable")
		return self.flow_table_object

	def __repr__(self):
		return "%s (id: %d)" % (self.name, self.openflow_id)

	def setController(self, cont):
		self.controller = cont

	def getOpenflowID(self):
		return self.openflow_id

	def setFaultInjectionCount(self, count):
		""" 
			this gets called after initTopology and
			when a fault action is executed 
		"""
		self.fault_injection_count = count

	# NOTE: function overloaded from Node.
	"""
		enqueue the packet in to the buffer on inport
		a packet arrived at the switch
		and enable a process_packet action
	"""	
	def enqueuePacket(self, packet, inport):
		self.log.debug("Queued packet %s on port %d" % (packet, inport))
		self.ports[inport].queueIn(packet)
		self.enableAction("process_packet", skip_dup=True)
		if self.fault_injection_count > 0:
#			self.enableAction("drop_packet", args=(inport,), skip_dup=True)
			self.enableAction("duplicate_packet", args=(inport,), skip_dup=True)
#			self.enableAction("reorder_packet", args=(inport,), skip_dup=True)

	def drop_packet(self, inport):
		""" Dequeues the first packet from the specified port and throws it away """
		more_packets = False
		pkt = self.getWaitingPacket(inport)
		if pkt != None and self.checkWaitingPacket(inport):
			more_packets = True

		return not (more_packets and self.fault_injection_count > 0)

	'''
		# copy the 1st packet at inport
		# and put the replica at the end of the queue
		# called when fault injected
	'''
	def duplicate_packet(self, inport):
		""" 
			Creates a copy of the the first packet on the 
			port and puts it on the end of the buffer 
		"""
		pkt = self.getWaitingPacket(inport)
		if pkt == None:
			self.log.debug("Empty buffer, no packets duplicated")
			return True

		self.ports[inport].in_buffer.insert(0, pkt)
		pkt2 = pkt.copy()
		pkt.fault_injection.append("HAS DUP")
		pkt2.fault_injection.append("DUP")
		self.ports[inport].in_buffer.append(pkt2)
		self.log.debug("Duplicated packet on port %d: %s" % (inport, pkt))

		return not self.fault_injection_count > 0

	def reorder_packet(self, inport):
		""" Appends the first packet at the end of the buffer """
		pkt = self.getWaitingPacket(inport)
		if pkt == None:
			return True

		pkt.fault_injection.append("REORD")
		self.ports[inport].in_buffer.append(pkt)

		return not self.fault_injection_count > 0

	def processActions(self, packet, actions, inport,entry=None):

		"""Process a set of actions on the packet"""
		for action in actions:							#_[[openflow.OFPAT_OUTPUT, [0, prt[0]]]]
			if action[0] == openflow.OFPAT_OUTPUT:		#_action[0] is the OFPAT OpenFlow Protocol Action
				port = action[1][1]						#_action[1][1] is the output port when action[0] is openflow.OFPAT_OUTPUT, action[1][0] is waste
				if port < openflow.OFPP_MAX:			#_general physical port
					peer = self.getPeer(port)
					if peer == None:
						continue # Skip disconnected port
					peer.enqueuePacket(packet.copy(), self.getPeerPort(port))
					testPoint("switch_sent_packet_on_port", switch=self, packet=packet, port=port)
				elif port == openflow.OFPP_FLOOD:		#_flood virtual port
					testPoint("switch_flood_packet_start", switch=self, packet=packet)
					for port in self.ports:
						if port == inport:
							continue # Skip the inport
						peer = self.getPeer(port)
						if peer == None:
							continue # Skip disconnected port
						peer.enqueuePacket(packet.copy(), self.getPeerPort(port))
						testPoint("switch_sent_packet_on_port", switch=self, packet=packet, port=port)
				elif port == openflow.OFPP_CONTROLLER:	# controller virtual port
					self.controller.enqueueQuery(self.openflow_id, None, packet.copy(), port, openflow.OFPR_ACTION)
				elif port == openflow.OFPP_TABLE:		#_flow table virtual port | does not achieved in NICE, so we don't needs to care about
					print "Packet-out sent this packet to FlowTable. NOT IMPLEMENTED"
				else:
					utils.crash("Unknown port action: 0x%x" % port)

			#Adding up an extra action type OFPAT_WAIT in the open flow protocol whose purpose is to hold the 
			#data packets inside the switch as the controller has already processed that entry field and the 
			# flow-mod messages will soon going to arrive and there is no need to send the packet packet_in 
			# to the controller
			elif action[0] == openflow.OFPAT_WAIT
				if entry==None
					utils.crash("parameter mismatch need a parameter entry ")
				delete_entry = entry
				flow_table.remove(delete_entry)
				entry.waiting_list.append(packet)
				flow_table.append(entry)

			#_OpenFlow set messeage, change header field of a packet. action[1] is the parameter needed by the action
			elif action[0] == openflow.OFPAT_SET_DL_SRC: # Set Ethernet source address
				packet.src = MacAddress(action[1])
			elif action[0] == openflow.OFPAT_SET_NW_SRC: # Set IPv4 source address
				packet.next.srcip = IpAddress(action[1]) # We assume the next is an ipv4
			elif action[0] == openflow.OFPAT_SET_DL_DST: # Set Ethernet destination address
				packet.ds1t = MacAddress(action[1])
			elif action[0] == openflow.OFPAT_SET_NW_DST: # Set IPv4 source address
				packet.next.dstip = IpAddress(action[1]) # We assume the next is an ipv4
			else:
				utils.crash("Action not implemented: %x" % action[0])

	def packetIsMatching(self, pkt, inport, attrs):
		"""Checks whether a packet is matching a specific table entry
		
		   attrs is a dictionary of attributes to match, missing attributes
		   are considered wildcarded
		"""

		"""
			in case of packet matching it depends upon weather the incoming packet is an regular packets
			or a pre-flow packet as in case of pre-flow the attributes to be matched are not the attributes of
			packet itself but are of the attribute that are of the next switch attributes that are need to be compared 
			with 
		"""

		pkt_type = pkt.find('pre_flow')
		if pkt_type !=None:
			pkt_attrs_tmp = of_util.extract_flow(pkt)
			"""
				TODO :
					In this need to make sure the attribute_next_switch parameter of the attribute of the packet 
					is the list so we need to merge that list into 						
			"""
			pkt_attrs = pkt_attrs_tmp[core.DL_NXT_SWITCH_ATTRIBS]
			pkt_attrs[core.IN_PORT] = inport

		else:
			pkt_attrs = of_util.extract_flow(pkt)
			pkt_attrs[core.IN_PORT] = inport

		skip_nw_src = False
		for a in attrs:
			if a == "nw_src_n_wild":
				mask = int(0xffffffff << attrs[a]) # see openflow.h in NOX for this definition of mask
				if pkt_attrs[core.NW_SRC] & mask != attrs["nw_src"]:
					return False
				else:
					skip_nw_src = True
			elif a == "nw_src" and skip_nw_src:
				continue
			elif attrs[a] != pkt_attrs[a]: # If this throws an exception, we have an usupported attribute
				return False
		return True
 

	#----------------CHANGE------------------------------------
		"""
			# 	This Function is used to match the flow table entries 
			# 	according to the attributes present in the packet , in this 
			#	just added one more check of the version number i.e. so before 
			# 	matching the flow attributes version nember of the entries will
			#	also be compared
		"""

	
	def matchFlowTable(self, pkt, inport):

		pkt_attrs = of_util.extract_flow(pkt)
		pkt_attrs[core.IN_PORT] = inport
		self.communicationObjectUsed(self, "flowTable_read", pkt_attrs)
		matching_entries = []
		final_matched_entries = []

		'''
			Checks wether that the incoming packet is a pre-flow packet 
			or regular packet , as if it is pre-flow packet version number 
			of the packet is compared with the entry's version number 
			before comparing the packets attributes
		'''
		pkt_type = pkt.find('pre_flow')

		if pkt_type != None :
			for entry in self.flow_table:
				if self.packetIsMatching(pkt, inport, entry.attrs):
					self.log.debug("*FTE: " + str(entry))
					matching_entries.append(entry)
				else:
					self.log.debug("FTE: " + str(entry))

			pkt_version_number = pkt_attrs[core.DL_VER_NUM]
		
			for matched_entry in matching_entries:
				if matched_entry.version_number == pkt_version_number :
					final_matched_entries.append(matched_entry) 

			'''
				Choosing among all the matched entries the entry which has version number greater than the 
				version number of the pre-flow packet and has highest priority and if there are some matching entries 
				in the flow table this means table is already updated with the suitable ACTION and there is no need
				to add a placeholder in the flow table
			'''	

			if len(final_matched_entries) == 0: # no match
				return False

			else:
				return True

		else:			
			for entry in self.flow_table:
				if self.packetIsMatching(pkt, inport, entry.attrs):
					self.log.debug("*FTE: " + str(entry))
					matching_entries.append(entry)
				else:
					self.log.debug("FTE: " + str(entry))

			if len(matching_entries) == 0: # no match
				return False

			else:
				matching_entries.sort(key=lambda x: x.priority, reverse=True)
				highest_priority = matching_entries[0].priority
				for matched_entry in matched_entries
					if matched_entry.priority == highest_priority
						final_matched_entries.append(matched_entry)

				final_matched_entries.sort(key=lambda x: x.version_number, reverse=True)
				entry = final_matched_entries[0]
				self.processActions(pkt, entry.actions, inport,entry)
				return True

	#-----------------------------------------------------------
	'''
		Processing up the packet-out messages depending up on the 
		wait bit present in the packet 
			if wait_bit = False no pre-flow packet
			else pre-flow packet

		the content of the pre-flow packet would be the 
		version number of the packets which has been received from the
		openflow controller.
	'''
	def processPacketOutMessage(self, command):
		self.log.debug("Processing a PacketOut: %s" % repr(command))
		if command.buffer_id != None:
			(packet, inport) = self.packet_store[command.buffer_id]
		else:
			packet = command.packet
			inport = command.inport

		# ------------------CHANGE------------------

		"""
			# got the packet and inport associated with the packet-out message
			# get the wait_bit and version_number_nxt_switch
		"""
		wait_bit = command.wait_bit
		attribute_next_switch = command.attribute_next_switch
		version_number_nxt_switch = command.version_number_nxt_switch

		if wait_bit == True and len(command.actions) > 0:
			preflow_packet = self.createPreFlowPacket(version_number_nxt_switch,attribute_next_switch)

			for action in command.actions:
				if action[0] == openflow.OFPAT_OUTPUT:		
					port = action[1][1]						
					if port < openflow.OFPP_MAX:			
						peer = self.getPeer(port)
						if peer == None:
							continue # Skip disconnected port
						sendPreFlowPacket(self , preflow_packet , port)

					elif port == openflow.OFPP_FLOOD:		#_flood virtual port
						testPoint("switch_flood_pre-flow_packet_start", switch=self, packet=packet)
						for port in self.ports:
							if port == inport:
								continue # Skip the inport
							peer = self.getPeer(port)
							if peer == None:
								continue # Skip disconnected port
							sendPreFlowPacket(self , preflow_packet , port)

		"""
			To check whether there are packets waiting to be processed that are put in the waiting queue
		"""
		pkt_attrs = of_util.extract_flow(packet)
		pkt_attrs[core.IN_PORT] = inport
		for entry in self.flow_table:
			if entry.attrs == pkt_attrs and entry.actions == OFPAT_WAIT and entry.version_number == SPECIAL_VERSION_NUMBER:
				if len(entry.waiting_list)>0:
					# 	This means there are some awaiting packets in the queue which are waiting 
					#	for the suitable actions from the controller
					for packet in entry.waiting_list :
						self.processActions(packet,command.actions, inport,entry=None):

		# ------------------------------------------
		# do the actions define in the packet-out message

		if len(command.actions) > 0:
			self.processActions(packet, command.actions, inport)
		else:
			self.log.debug("Dropping packet with empty action list")
		# release the buffer that the buffer_id pointed to
		if command.buffer_id != None:
			self.releaseBuffer(command.buffer_id)
		return


	def processFlowTableModification(self, command):
		self.communicationObjectUsed(self, "flowTable_write", command.arguments["attrs"])
		self.log.debug("Processing a FlowTableModification command: %s" % repr(command))


		#--------------------------CHANGE-------------------------------------------
		
			"""
				1)	Adding up an extra field in the flow table entry with the version number 
					whenever flow-mod message is received

				2) 	Sending up the pre-flow packet whenever flow-mod message with OFPFC_ADD command
					is received

				3)	Updating the action field of the flow tabel entry with the action received as in the 
					flow-mod message
			"""
		#---------------------------------------------------------------------------


		wait_bit = command.wait_bit
		attribute_next_switch = command.attribute_next_switch
		version_number_nxt_switch = command.version_number_nxt_switch

		if wait_bit == True and len(command.arguments["actions"]) > 0:
			preflow_packet = self.createPreFlowPacket(version_number_nxt_switch,attribute_next_switch)
			for action in command.arguments["actions"]:
				if action[0] == openflow.OFPAT_OUTPUT:		
					port = action[1][1]						
					if port < openflow.OFPP_MAX:			
						peer = self.getPeer(port)
						if peer == None:
							continue # Skip disconnected port
						sendPreFlowPacket(self , preflow_packet , port)

					elif port == openflow.OFPP_FLOOD:		#_flood virtual port
						testPoint("switch_flood_pre-flow_packet_start", switch=self, packet=packet)
						for port in self.ports:
							if port == inport:
								continue # Skip the inport
							peer = self.getPeer(port)
							if peer == None:
								continue # Skip disconnected port
							sendPreFlowPacket(self , preflow_packet , port)

		if command.command == openflow.OFPFC_ADD:
			# TODO: idle_timeout, hard_timeout
			entry_added = False
			for entry in self.flow_table:
				if entry.attrs == command.arguments["attrs"] and entry.actions == OFPAT_WAIT and (entry.version_number == command.version_number or entry.version_number == SPECIAL_VERSION_NUMBER):
					entry_added = True
					entry.actions = command.arguments["actions"]
					entry.priority = command.arguments["priority"]
					entry.version_number = command.version_number

					"""
						To check whether there are packets waiting to be processed that are put in the waiting queue
					"""
					if len(entry.waiting_list)>0:
						# This means there are some awaiting packets in the queue which are waiting for the suitable actions from the controller
						for packet in entry.waiting_list :
							#------------------------DOUBT------------------
							inport ===???
							self.processActions(packet,entry.actions, inport,entry=None):
							#-----------------------------------------------

			if entry_added == False :
				new_flow_table_entry = FlowTableEntry(command.arguments["attrs"], command.arguments["actions"], command.arguments["priority"],command.version_number)
				self.flow_table.append(new_flow_table_entry)

			self.flow_table.sort()
			if self.expire_entries:
				self.enableAction("expire_entry", new_flow_table_entry)
			# Process the packet specified in buffer_id
			buf_id = command.arguments["buffer_id"]
			if buf_id != None:
				if buf_id not in self.packet_store:
					v = Violation(None, "Trying to access buffer %d %s" % (buf_id, self.packet_store))
					reportViolation(v)
				(packet, inport) = self.packet_store[buf_id]
				self.matchFlowTable(packet, inport)
				self.releaseBuffer(buf_id)

		elif command.command == openflow.OFPFC_DELETE:
			attrs = command.arguments["attrs"]
			for e in self.flow_table:
				if e.attrs == attrs:
					self.log.debug("Deleting flow entry %s" % e)
					del self.flow_table[self.flow_table.index(e)]
					if command.arguments.has_key("flags") and openflow.OFPFF_SEND_FLOW_REM in command.arguments["flags"]:
						msg = openflow.OfpFlowRemoved()
						msg.priority = e.priority
						msg.reason = openflow.OFPRR_DELETE
						msg.table_id = 0
						msg.duration_sec = 0
						msg.duration_nsec = 0
						msg.idle_timeout = 0
						msg.packet_count = 0
						msg.byte_count = 0
						msg.match = e.attrs # match attributes
						self.controller.flowRemoved(msg)

		elif command.command == openflow.OFPFC_DELETE_STRICT:
			attrs = command.arguments["attrs"]
			priority = command.arguments["priority"]
			for e in self.flow_table:
				if e.attrs == attrs and e.priority == priority:
					self.log.debug("Deleting flow entry %s" % e)
					del self.flow_table[self.flow_table.index(e)]
					if command.arguments.has_key("flags") and openflow.OFPFF_SEND_FLOW_REM in command.arguments["flags"]:
						msg = openflow.OfpFlowRemoved()
						msg.priority = e.priority
						msg.reason = openflow.OFPRR_DELETE
						msg.table_id = 0
						msg.duration_sec = 0
						msg.duration_nsec = 0
						msg.idle_timeout = 0
						msg.packet_count = 0
						msg.byte_count = 0
						msg.match = e.attrs # match attributes
						self.controller.flowRemoved(msg)
					break

	#------------------CHANGE------------------------------

	'''
		createPreFlowPacket function is used to create ethernet 
		packet which has the version number 
	'''
	def createPreFlowPacket(self ,version_number, attribute_next_switch):
		eth = ethernet.ethernet("pre_flow",version_number, attribute_next_switch)
		return eth 


	def sendPreFlowPacket(self , packet , port):
		peer = self.getPeer(port)
		peer.enqueuePacket(packet.copy(), self.getPeerPort(port))
		testPoint("switch_sent_pre-flow_packet_on_port", switch=self, packet=packet, port=port)
		return

	#-------------------------------------------------------	


	def acquireBuffer(self):
		if len(self.buffers) == 0:
			self.buffers.append(self.next_buffer_id)
			self.next_buffer_id = self.next_buffer_id + 1
		return self.buffers.pop()
	
	def releaseBuffer(self, buffer_id):
		del self.packet_store[buffer_id]
		self.buffers.append(buffer_id)

	def enqueueCommand(self, command):
		self.command_queue.append(command)
		self.enableAction("process_command", skip_dup=True)
		testPoint("switch_enqueue_command", switch=self, command=command)
		self.log.debug("Queued command: %s" % repr(command))

	def expire_entry(self, entry):
		self.communicationObjectUsed(self, "flowTable_write", entry.attrs)
		del self.flow_table[self.flow_table.index(entry)]
		if entry.send_flow_rem:
			msg = openflow.OfpFlowRemoved()
			msg.priority = e.priority
			msg.reason = openflow.OFPRR_HARD_TIMEOUT # could be also IDLE_TIMEOUT
			msg.table_id = 0
			msg.duration_sec = 0
			msg.duration_nsec = 0
			msg.idle_timeout = 0
			msg.packet_count = 0
			msg.byte_count = 0
			msg.match = entry.attrs # match attributes
			self.controller.flowRemoved(msg)
		return True

	def getWaitingPacket(self, port_name):
		port = self.ports[port_name]
		if len(port.in_buffer) > 0:
			pkt = port.in_buffer.pop(0)
			return pkt
		else:
			return None

	"""
		This help in processing the packet received from the port 
		depending on the pkt_type = regular pkt / Pre-flow Packet 
		action is decided
	"""		

	#------------------------------CHANGE-------------------------
	
	def process_packet(self):
		""" Dequeues the first packet from all ports and processes it """
#		import pdb; pdb.set_trace()
		more_packets = False
		for p in self.ports:
			pkt = self.getWaitingPacket(p)
			if pkt == None:
				continue
			elif self.checkWaitingPacket(p):
				more_packets = True

			self.log.debug("Processing packet %s" % pkt)
			testPoint("switch_process_packet", switch=self, packet=pkt, port=p)
			
			if not self.matchFlowTable(pkt, p):	

				pkt_type = pkt.find('pre_flow')
				if pkt_type != None:
					'''
						CASE :		
							handling of the packet type where the packet is a pre-flow packet 
							and handling the case where the pre-flow packet arrives at the 
							switch and make an entry in the flow table with the default action 
							as OFPAT_WAIT 
					'''
					pkt_attrs_tmp = of_util.extract_flow(pkt)
					pkt_attrs = pkt_attrs_tmp[core.DL_NXT_SWITCH_ATTRIBS]
					pkt_attrs[core.IN_PORT] = p
					e = FlowTableEntry(pkt_attrs,OFPAT_WAIT, None , pkt_attrs[core.DL_VER_NUM])
					self.flow_table.append(e)	

				else:
					'''
						CASE : 	
							handling of the packet type where the packet is a regular packet 
							and handling the case where the regular packet arrives at the 
							switch and there is no entry in the switch flow table for the particular
							match attributes --- In this case we need to send the packet to the controller

							At the same time the packet-entry is added in the flow table for avoiding the packet with 
							the same attribute to be sent to the controller again 
					'''
					#---------------DOUBT----------------------------
					#pkt_attrs_tmp = of_util.extract_flow(pkt)
					pkt_attrs = of_util.extract_flow(pkt)
					pkt_attrs[core.IN_PORT] = p
					e = FlowTableEntry(pkt_attrs,OFPAT_WAIT, None , "SPECIAL_VERSION_NUMBER")
					self.flow_table.append(e)						
					#-----------------------------------------------------------

					buffer_id = self.acquireBuffer()
					self.controller.enqueueQuery(self.openflow_id, buffer_id, pkt, p, openflow.OFPR_NO_MATCH)
					self.packet_store[buffer_id] = (pkt, p)
					self.log.debug("Queued query to controller")

		return not more_packets

	#-----------------------------------------------------------
	def process_command(self):
		""" Process a command from the controller """
		command = self.command_queue.pop(0)
		if isinstance(command, PacketOutMessage):
			self.processPacketOutMessage(command)
		elif isinstance(command, FlowTableModificationMessage):
			self.processFlowTableModification(command)
		else:
			utils.crash("Switch received an unknown command: %s" % command)
		return len(self.command_queue) == 0

	def __getstate__(self):
		filtered_dict = Node.__getstate__(self)
		filtered_dict["command_queue"] = self.command_queue

		filtered_dict["flow_table"] = []
		for j in self.flow_table:
			filtered_dict["flow_table"].append(j.__getstate__())

		if self.ALWAYS_NEW_STATE:
			self.state_cnt += 1
			filtered_dict["state_cnt"] = self.state_cnt

		return filtered_dict

